#include "..\include\Game.hpp"
#include "../include/Enemy.hpp"
#include "stdio.h"
#include <string>
#include "../include/Tools.hpp"
#include "..\include\Hero.hpp"
#include "..\include\Enemy.hpp"
#include "..\include\Application.hpp"
#include "..\include\SpritesManager.hpp"
#include "..\include\BackgroundManager.hpp"
#include "../include/DecorElement.hpp"
#include "../build/BG_BOTTOM_DAY_1_1.h"
#include "../build/BG_BOTTOM_DAY_1_2.h"
#include "../build/BG_BOTTOM_NIGHT_1_1.h"
#include "../build/BG_BOTTOM_NIGHT_1_2.h"
#include "../build/BG_TOP_DAY_DAY_1.h"
#include "../build/BG_TOP_NIGHT_NIGHT_1.h"
#include "../include/ManaBottle.hpp"
#include <maxmod9.h>
#include "soundbank.h"
#include "soundbank_bin.h"

Game::Game(BackgroundManager* _bgManager)
{

	spritesManager = 0;
	backgroundManager = _bgManager;
}


Game::~Game(void)
{
}

void Game::launchGame(Application* app, int level){
	iprintf("launch\n");
	std::string levelStr;
	if (level == 1) levelStr = "/level1/";
	else if (level == 2) levelStr = "/level2/";
	else if (level == 3) levelStr = "/level3/";
	else levelStr = "/level4/";

	currentLevel = level;

	parseBackgroundFile(levelStr + "backgrounds.txt", backgroundSwitch);
	iprintf("launch1\n");
	std::vector<EnemyFile> enemies;
	std::vector<ManaFile> mana;
	std::vector<DecorFile> decor;
	parseSpriteFile(levelStr + "sprites.txt", decor, enemies, mana);
	iprintf("launch2\n");
	std::vector<int> unlockedTransfos;
	iprintf("launch3\n");
	parseInfoFile(levelStr + "infos.txt", levelSize, unlockedTransfos);
	iprintf("launh4\n");

	std::vector<int> forbiddenTransfos;
	inverseTransfos(unlockedTransfos, forbiddenTransfos);

	spritesManager = new SpritesManager();
	spritesManager->initOAM();

	spritesManager->buildSprites(decor, enemies, mana, levelSize, forbiddenTransfos);

	spritesManager->spriteScroll = true;
	backgroundManager->backgroundScroll = true;

	bgSpeedDif = 2;
	scrollValue = 0;
	pursuerScrollValue = -50;
	pursuerSpeed = 550;

	spritesManager->spriteSpeed = spritesManager->speedBase / 2; // 2 is the speed value of Hero Human
	backgroundManager->backgroundSpeed = bgSpeedDif*spritesManager->spriteSpeed;

	sunCycle = DAY;

	timerStart(0, ClockDivider_1024, 0, NULL);
	timerStart(1, ClockDivider_1024, 0, NULL);
	timerStart(2, ClockDivider_1024, 0, NULL);

	backgroundManager->initGameBG(app);

	//spritesManager->hero->transform(3, backgroundManager); 
}

bgData Game::getBgToLoad(){

	bgData data;
	data.data = BG_TOP_DAY_DAY_1Bitmap;
	data.length = BG_TOP_DAY_DAY_1BitmapLen;

	return data;
}

void Game::update(Application* app){
	spritesManager->updateOAM();
	scroll(app);
	spritesManager->hero->animate();
	spritesManager->updateHud(scrollValue, pursuerScrollValue, levelSize);

	for(unsigned int i=0; i<spritesManager->enemies.size(); i++){
		if(spritesManager->enemies[i]->isAlive())
			if (spritesManager->enemies[i]->displayed){
				spritesManager->enemies[i]->animate();
			}
	}

	falling = true;
	checkCollision(BOTTOM, app);
	heroFall(app);
	checkPursuit(app);

	if(spritesManager->hero->getHealth() == 0){
		app->game->reset(app);
		app->state = GAME_OVER;
	}

	//InvincibleTick
	if(spritesManager->hero->getInvincible() && spritesManager->hero->getInvincibleTicks() > 100){
		spritesManager->hero->setInvincible(false);
		spritesManager->hero->resetInvincibleTicks();
	}

	else if(spritesManager->hero->getInvincible())spritesManager->hero->setInvincibleTicks(1);

	//std::cout << "pursuer "  << pursuerScrollValue << "Rith "  << scrollValue << std::endl;
}

void Game::scroll(Application* app){
	if(spritesManager->spritesTicks > spritesManager->spriteSpeed){
		scrollValue++;			
		checkEndOfLevel(app);
		checkDayAndNight(app);
	}
	spritesManager->scrollSprites();


	if (backgroundManager->loadedBG == GAME) {
		backgroundManager->scrollAll(this, app);
	}
}

void Game::checkDayAndNight(Application* app){

	std::vector<int>::iterator _it = std::find(backgroundSwitch.begin(), backgroundSwitch.end(), scrollValue);
	if ( _it != backgroundSwitch.end()){
		if(sunCycle==DAY){
			backgroundManager->loadBackground(BG_TOP_NIGHT_NIGHT_1Bitmap, BG_TOP_NIGHT_NIGHT_1BitmapLen, 2, TOP, true, app);
			backgroundManager->loadBackground(BG_BOTTOM_NIGHT_1_1Bitmap, 0, 2, BOTTOM, false, app);
			backgroundManager->loadBackground(BG_BOTTOM_NIGHT_1_2Bitmap, 0, 3, BOTTOM, false, app);
			sunCycle=NIGHT;
		}
		else {
			backgroundManager->loadBackground(BG_TOP_DAY_DAY_1Bitmap, BG_TOP_DAY_DAY_1BitmapLen, 2, TOP, true, app);
			backgroundManager->loadBackground(BG_BOTTOM_DAY_1_1Bitmap, 0, 2, BOTTOM, false, app);
			backgroundManager->loadBackground(BG_BOTTOM_DAY_1_2Bitmap, 0, 3, BOTTOM, false, app);
			sunCycle=DAY;
		}
	}
}

//TODO : typeid to check if Mana or decorElement
void Game::checkCollision(Screen pos, Application* app){

	unsigned int heroX =0;
	unsigned int heroY =0;

	if(spritesManager->hero->getScreen() == BOTTOM){
		SpriteEntry hero = spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID];
		heroX = hero.x;
		heroY = hero.y;

		std::map<unsigned int, bool>::iterator _it;

		for(_it = spritesManager->manaBottlesOAMID.begin(); _it != spritesManager->manaBottlesOAMID.end(); ++_it){
			if( _it->second == true){
				unsigned int manaX, manaY;	
				manaX = spritesManager->oam->oamBuffer[_it->first].x;
				manaY = spritesManager->oam->oamBuffer[_it->first].y;

				if(manaX+16 <= heroX+32 && manaX+16 >= heroX && manaY+16 >= heroY && manaY+16 <= heroY+32){
					spritesManager->hero->updateMana();
					mmEffect( SFX_MANA );
					spritesManager->oam->oamBuffer[_it->first].isHidden = true;
					_it->second = false;
				}
			}//if display
		}//for manaOAMID

		for(_it = spritesManager->enemiesOAMID.begin(); _it != spritesManager->enemiesOAMID.end(); ++_it){
			if( _it->second == true){
				unsigned int enemyX, enemyY;	
				enemyX = spritesManager->oam->oamBuffer[_it->first].x;
				enemyY = spritesManager->oam->oamBuffer[_it->first].y;

				if(enemyX+16 <= heroX+32 && enemyX+16 >= heroX && enemyY+16 >= heroY && enemyY+16 <= heroY+32){
					//Si on est pas un zombie
					if(!spritesManager->hero->getInvincible() && spritesManager->hero->getTransformID()!=2 ){
						spritesManager->hero->updateHealth(-1);
						spritesManager->hero->setInvincible(true);			
					}
					else if(spritesManager->hero->getTransformID()==2) {

						
						std::vector<Enemy*>::iterator _it2;
						for(_it2 = spritesManager->enemies.begin(); _it2 != spritesManager->enemies.end(); ++_it2) {

							Enemy* en = *_it2;
							if(en->OAM_ID == _it->first){en->setAlive(false);
							spritesManager->oam->oamBuffer[_it->first].isHidden = true;
							spritesManager->oam->oamBuffer[_it->first+1].isHidden = true;
							spritesManager->oam->oamBuffer[_it->first+2].isHidden = true;
							spritesManager->enemiesOAMID.find(_it->first)->second = false;
							spritesManager->enemiesOAMID.find(_it->first+1)->second = false;
							spritesManager->enemiesOAMID.find(_it->first+2)->second = false;
							//std::cout << "THERE "<< std::endl;
							//std::cout << "HEALTH "<< spritesManager->hero->getHealth()<< std::endl;
							//std::cout << "HEALTH "<< spritesManager->hero->getHealth()<< std::endl;
							
							}
							spritesManager->hero->updateHealth(1);
						}
						
						
					}
				}
			}//if display
		}//for enemiesOAMID
		
		//std::cout << "Jump " <<spritesManager->hero->getJumpDir() <<std::endl;

		bool onPlatform = false;
		for(_it = spritesManager->decorElementsOAMID.begin(); _it != spritesManager->decorElementsOAMID.end(); ++_it){
			if( _it->second == true){

				unsigned int obstacleX, obstacleY;	
				obstacleX = spritesManager->oam->oamBuffer[_it->first].x;
				obstacleY = spritesManager->oam->oamBuffer[_it->first].y;

				//Upper left corner
				if(obstacleX <= heroX+32 && obstacleX >= heroX && obstacleY+6 >= heroY+32 && obstacleY+6 <= heroY+33 && spritesManager->hero->getJumpDir() != GO_UP){
				
					falling = false;
					spritesManager->hero->setJumping(false);
					spritesManager->hero->setDoubleJump(false);
					spritesManager->hero->setJumpDirection(ON_PLATEFORM);
					onPlatform = true;
				}

				//Upper right corner
				else if(obstacleX+32 <= heroX+32 && obstacleX+32 >= heroX && obstacleY+6 >= heroY+32 && obstacleY+6 <= heroY+33 && spritesManager->hero->getJumpDir() != GO_UP){
			
					falling = false;
					spritesManager->hero->setJumping(false);
					spritesManager->hero->setDoubleJump(false);
					spritesManager->hero->setJumpDirection(ON_PLATEFORM);
					onPlatform = true;
				}
			}
		}

		//If we get to the end of a plateform
		if(onPlatform == false && spritesManager->hero->getJumpDir() == ON_PLATEFORM) spritesManager->hero->setJumpDirection(GO_DOWN);

	}//End if BOTTOM

	else if(spritesManager->hero->getScreen() == TOP){

		SpriteEntry hero = spritesManager->oamSub->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_TOP_ID];
		heroX = hero.x;
		heroY = hero.y;

		std::map<unsigned int, bool>::iterator _it;

		for(_it = spritesManager->manaBottlesTopOAMID.begin(); _it != spritesManager->manaBottlesTopOAMID.end(); ++_it){
			if( _it->second == true){
				unsigned int manaX, manaY;	
				manaX = spritesManager->oamSub->oamBuffer[_it->first].x;
				manaY = spritesManager->oamSub->oamBuffer[_it->first].y;

				if(manaX+16 <= heroX+32 && manaX+16 >= heroX && manaY+16 >= heroY && manaY+16 <= heroY+32){
					spritesManager->hero->updateMana();
					spritesManager->oamSub->oamBuffer[_it->first].isHidden = true;
					_it->second = false;
				}
			}//if display
		}//for manaOAMID
		bool onPlatform = false;
		for(_it = spritesManager->decorElementsTopOAMID.begin(); _it != spritesManager->decorElementsTopOAMID.end(); ++_it){
			if( _it->second == true){

				unsigned int obstacleX, obstacleY;	
				obstacleX = spritesManager->oamSub->oamBuffer[_it->first].x;
				obstacleY = spritesManager->oamSub->oamBuffer[_it->first].y;

				//Coin gauche
				if(obstacleX <= heroX+32 && obstacleX >= heroX && obstacleY+4 >= heroY+32 && obstacleY+4 <= heroY+33 && spritesManager->hero->getJumpDir() != GO_UP){
				
					falling = false;
					spritesManager->hero->setJumping(false);
					spritesManager->hero->setDoubleJump(false);
					spritesManager->hero->setJumpDirection(ON_PLATEFORM);
					onPlatform = true;
					
				}

				//Coin droit
				if(obstacleX+32 <= heroX+32 && obstacleX+32 >= heroX && obstacleY+4 >= heroY+32 && obstacleY+4 <= heroY+33 && spritesManager->hero->getJumpDir() != GO_UP){
			
					falling = false;
					spritesManager->hero->setJumping(false);
					spritesManager->hero->setDoubleJump(false);
					spritesManager->hero->setJumpDirection(ON_PLATEFORM);
					onPlatform = true;
					
				}
			}
		}
		//IF we get to the end of a plateform
		if(onPlatform == false && spritesManager->hero->getJumpDir() == ON_PLATEFORM) spritesManager->hero->setJumpDirection(GO_DOWN);
	
	}//End else if TOP

}

void Game::heroFall(Application * app){

	//std::cout << "Jump " <<spritesManager->hero->getJumping() << std::endl;
	
	if(spritesManager->hero->getJumpDir() !=GO_DOWN || spritesManager->hero->getFlying())falling = false;

	//std::cout << "falling" << falling << std::endl;

	if(spritesManager->hero->getScreen() == BOTTOM){

		if(falling && spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID].y < 129){

			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID].y += 2;
			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID+1].y += 2;
			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID+2].y += 2;

		}
		else if(falling && spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID].y < 130){

			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID].y += 1;
			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID+1].y += 1;
			spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID+2].y += 1;
			spritesManager->hero->setJumping(false);
			spritesManager->hero->setDoubleJump(false);
			spritesManager->hero->setJumpDirection(TO_GROUND);

		}

		else if(falling  && spritesManager->oam->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_ID].y == 130){
		spritesManager->hero->setJumping(false);
		spritesManager->hero->setDoubleJump(false);
		spritesManager->hero->setJumpDirection(TO_GROUND);
	
		}

	}//End if BOTTOM
	else if(spritesManager->hero->getScreen() == TOP){
		if(falling && spritesManager->oamSub->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_TOP_ID].y < 193){

			spritesManager->oamSub->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_TOP_ID].y += 2;
			spritesManager->oamSub->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_TOP_ID+1].y += 2;
			spritesManager->oamSub->oamBuffer[spritesManager->hero->charDatas[spritesManager->hero->getTransformID()]->firstOAM_TOP_ID+2].y += 2;
		}
	}
}

void Game::checkPursuit(Application* app){

	pursuerTicks += timerElapsed(2);

	if(pursuerTicks > pursuerSpeed){
		pursuerScrollValue++;
		//iprintf("pursuer : %i \n", pursuerScrollValue);
		pursuerTicks = 0;
	}

	if(pursuerScrollValue >= scrollValue){

		app->state = GAME_OVER;	
		reset(app);
	}
}

void Game::checkEndOfLevel(Application* app){
	if (scrollValue >= levelSize){
		reset(app);
		saveFinishedLevel(currentLevel);
		if (currentLevel == 3) app->state = GAME_WON;	
		else if (currentLevel == 4) app->state = GAME_WON;	
		else app->state = LEVEL_WON;
	}
}

void Game::reset(Application * app){


	OAMTable EmptyOAM;
	dmaCopyHalfWords(SPRITE_DMA_CHANNEL,
		EmptyOAM.oamBuffer,
		OAM,
		SPRITE_COUNT * sizeof(SpriteEntry));

	dmaCopyHalfWords(SPRITE_DMA_CHANNEL,
		EmptyOAM.oamBuffer,
		OAM_SUB,
		SPRITE_COUNT * sizeof(SpriteEntry));


	backgroundSwitch.clear();
	backgroundManager->reset();

	delete spritesManager;

}
